iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
Software Development

Datomic,內建事件溯源的資料庫。系列 第 4

一些 SQL 資料庫共同的缺陷 - 續

  • 分享至 

  • xImage
  •  

鎖與隔離的問題

SQL 資料庫的寫入,必須要加上鎖 (lock) 才能確保一致性。

參考下方的示意圖,A 與 B 是兩條執行緒,如果它們同時對同一筆資料做更新的動作,很有可能會造成錯誤。理論上,A 與 B 都成功完成的結果應該要得到 3。實際上,如果兩個操作沒有充分地隔離而互相影響,就有可能得到結果為 1 或是 2

[table X 的 i row j column 的初始值為 0]
A ------->  [對 table X 的 i row j column + 1]
B ------->  [對 table X 的 i row j column + 2] 

-------------------> 時間軸

這個加鎖的開銷非常高,也有統計指出,在高併發的條件之下,資料庫的效能有很大一部分用於鎖定資料表。

因為加鎖的開銷高的關系,SQL 資料庫預設的選項通常是提供樂觀鎖 (optimistic lock) ,也就是,能不要加鎖就不加鎖,少數的寫入失敗交給資料庫使用者去自行處理。這種機制一方面提高了效能,另一方面,又讓隔離沒有辦法做到 100% ,所以與資料互動的高併發程式,往往會充滿了各式各樣的奇技淫巧。

Schema 難以查詢

  • 假如在某個 Postgres 資料庫裡,有若干張表都有 id 這個欄位,要下什麼查詢,可以找出所有這些表?

這一類的問題,在 Postgres 還算是有不錯的權宜之計,查詢「系統資料表」即可。

SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'id'
ORDER BY table_schema, table_name;

在 SQL 資料庫,資料表綱要 (schema) 本身並不是資料,而是指令。然而,在實務的工作之中,我們往往都還是會在某個關鍵時刻需要將資料表綱要視為資料來做查詢。

SQL 的使用者如果一開始不知道多數的廠商通常都會設計自家的「系統資料表」時,往往會在這個部分覺得非常卡關。

Schema 僵化且固定

如果有一張表 A,裡頭有三個欄位:

;; 表 A
id, item_name, price

總是偶爾會發生一種情況,我們很想要在表 A 裡,放入第四個欄位 has_discount,且有一些列 (row) 會有第四個欄位,有一些則會沒有。

這時通常有兩種作法:

  1. 修改表 A 的資料表綱要 (schema) ,增加第四個欄位,且容許 NULL 值。
  2. 新增另一張表 B,讓它包含第四個欄位,並且讓它指向表 A,日後需要第四欄位時,就用 join 來處理。
;; 表 B 
A_id, has_discount

這兩種作法都有小小缺點:

  • 第一種作法的問題在於 NULL,因為 NULL 在此處的概念是『沒有』。然而,很多資料庫的教材,都會講 NULL 也可以用來表示不合理的值,也因此,一旦使用了 NULL 值,就開始讓資料庫的語意顯得有點不清晰。
  • 第二種作法很有一般性,只是說這個方式有點要頭腦轉一下,缺點是有點殺雞用牛刀。

阻抗不匹配 (Impedance mismatch)

阻抗不匹配一詞來自於電子工程,指的是兩種不同電子系統在相互作用時出現的阻抗不相容。後來,這個詞彙被引伸,用來描述在物件導向程式設計 (OOP) 與 SQL 資料庫的場景中,物件導向模型和關聯式資料模型之間的概念差異,因而導致的開發困難。

不匹配有許多形式,其中一種是 N+1 問題。

什麼是 N+1 問題呢?應用軟體工程師若使用 Ruby on Rails 之類的開發框架來輔助產生 SQL 來取代手刻 SQL,由於 SQL 是由 Ruby on Rails 自動生成的,它預設不會刻意去做效能的最佳化,這時就會有 N+1 問題發生。舉例來講,如果要做的操作是從學校 (school) 這個資料庫裡,取出某個班級 (class) 的所有學生 (student) 的資料,並且對每個學生的數學成績做開根號乘以十。自動生成的 SQL 有可能會是這樣子做:

  • 產生 1 個 SQL 查詢去取得某班所有的學生的資料,假設得到 N 筆學生的資料
  • 產生 N 個 SQL 查詢去取得每個學生的數學成績。

一旦考慮到 IO 非常慢的這個事實之後,上述自動生成的 N+1 個 SQL 自然是相當地低效率。高效的作法,則是只產生一個 SQL 查詢,它內含了 join ,所以可以只用一個 SQL 查詢,就做完步驟1與步驟 2 的工作,即只用一個 SQL 查詢就取回所有學生的數學成績,而這種作法才是符合 SQL 資料庫概念模型的作法。

然而,嚴格來說,上述的 N+1 問題如果只是效能差一些,其實是可以忽略不計。真正不能忍受的錯誤其實是在資料的一致性上。由於產生了 N+1 個 SQL 查詢而非 1 個,這 N+1 個 SQL 查詢自然是在不同的時間點運行,如果中途資料發生了修改,那就會造成資料的不一致,進而造成邏輯錯誤。

其它資源:

  1. 歡迎訂閱PruningSuccess電子報,主要談論軟體開發、資料處理、資料分析等議題。
  2. 歡迎加入Clojure社群

上一篇
一些 SQL 資料庫共同的缺陷
下一篇
Datomic 所屬的資料庫類別
系列文
Datomic,內建事件溯源的資料庫。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言